package com.testcolision;

import java.io.InputStream;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g3d.decals.DecalBatch;
import com.badlogic.gdx.graphics.g3d.loaders.obj.ObjLoader;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.gdx.utils.PerspectiveCamController;
import com.testcolision.simulation.RaycastWheel;
import com.testcolision.simulation.RigidBodyState;
import com.testcolision.simulation.TerrainSimulation;
import com.testcolision.simulation.Vehicle;

public class Renderer {

	private SpriteBatch spriteBatch;

	private Mesh constraintSphereMesh;
	private Mesh wheelMesh;
	private Texture carTexture;
	private Mesh planeMesh;
	private Mesh carMesh;
	private Texture sandboxTexture;

	private Mesh streetMesh;

	private ParticleEffect starfield;

	private PerspectiveCamera camera;
	private PerspectiveCamController controller;
	private Texture backgroundTexture;
	private DecalBatch decalBatch;
	private Texture skyTexture;
	private Mesh cubeMesh;

	public Renderer() {
		try {

			InputStream in = Gdx.files.internal("data/sphere.obj").read();
			constraintSphereMesh = ObjLoader.loadObj(in);
			constraintSphereMesh.scale(0.2f, 0.2f, 0.2f);
			in.close();

			in = Gdx.files.internal("data/car.obj").read();
			carMesh = ObjLoader.loadObj(in, true);
			carMesh.scale(2f, 2f, 2f);
			in.close();

			in = Gdx.files.internal("data/cube.obj").read();
			cubeMesh = ObjLoader.loadObj(in, true);
			cubeMesh.scale(50f, 50f, 50f);
			in.close();

			in = Gdx.files.internal("data/wheel.obj").read();
			wheelMesh = ObjLoader.loadObj(in, true);
			wheelMesh.scale(2f, 2f, 2f);
			in.close();

			in = Gdx.files.internal("data/race_track.obj").read();
			streetMesh = ObjLoader.loadObj(in, true);
			streetMesh.scale(2f, 2f, 2f);
			in.close();

			starfield = new ParticleEffect();
			starfield.load(Gdx.files.internal("data/starfield"), Gdx.files.internal("data"));
			starfield.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
			starfield.start();

			sandboxTexture = new Texture(Gdx.files.internal("data/race_track.png"), Format.RGBA8888, true);
			sandboxTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);

			skyTexture = new Texture(Gdx.files.internal("data/cubemap.png"), Format.RGB565, false);
			skyTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);

			backgroundTexture = new Texture(Gdx.files.internal("data/planet.jpg"), Format.RGB565, true);
			backgroundTexture.setFilter(TextureFilter.MipMap, TextureFilter.Linear);

			carTexture = new Texture(Gdx.files.internal("data/car.png"), Format.RGB565, true);
			carTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);

			spriteBatch = new SpriteBatch();
			decalBatch = new DecalBatch();

			camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
			camera.far = 1000;

			controller = new PerspectiveCamController(camera);
			Gdx.input.setInputProcessor(controller);

			GL10 gl = Gdx.graphics.getGL10();
			gl.glDisable(GL10.GL_DITHER);
			gl.glEnable(GL10.GL_DEPTH_TEST);

			// gl.glDisable(GL10.GL_LIGHTING);
			// gl.glDisable(GL10.GL_LIGHT0);
			// gl.glDisable(GL10.GL_COLOR_MATERIAL);
			// gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public void render(TerrainSimulation simulation, float delta) {

		GL10 gl = Gdx.graphics.getGL10();
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

		RigidBodyState vehicleState = simulation.r.rigidBodyStates[simulation.r.sourceStateIndex];

		setProjectionAndCamera(gl, vehicleState.position);

		gl.glEnable(GL10.GL_TEXTURE_2D);
		gl.glDisable(GL10.GL_DEPTH_TEST);
		renderSkybox(gl, vehicleState.position);

		gl.glEnable(GL10.GL_DEPTH_TEST);
		// gl.glEnable(GL10.GL_CULL_FACE);
		// gl.glCullFace(GL10.GL_BACK);

		// setLighting(gl, true);
		renderCar(gl, vehicleState, 0);
		renderWheels(gl, simulation.r, vehicleState, simulation.r.raycastWheels, simulation.tire_comp, delta);

		renderScene(gl, simulation.terrain.terrainMesh);

		gl.glEnable(GL10.GL_BLEND);
		gl.glBlendFunc(GL10.GL_ALPHA, GL10.GL_SRC_ALPHA);
		gl.glDisable(GL10.GL_CULL_FACE);
		// renderAssets(gl);

	}

	private void renderSkybox(GL10 gl, Vector3 position) {
		skyTexture.bind();
		gl.glPushMatrix();

		gl.glTranslatef(position.x, position.y, position.z);

		cubeMesh.render(GL10.GL_TRIANGLES);

		gl.glPopMatrix();
	}

	Matrix4 rotationMatrix = new Matrix4();
	float wheelRotationAngle = 0;
	float wheelngularVelocity = 0;
	Vector3 wheelPosition = new Vector3();
	Vector3 inverseUpVector = new Vector3(0, -1, 0);

	private void renderWheels(GL10 gl, Vehicle r, RigidBodyState vehicleState, RaycastWheel[] raycastWheels, float[] tire_comp, float delta) {
		
			
			
	wheelngularVelocity =  vehicleState.linearVelocity.dot(raycastWheels[3].frontVec.cpy().rot(vehicleState.orientationMatrix))/2;
	wheelRotationAngle -= wheelngularVelocity;

		
		for (int i = 0; i < 4; i++) {
			gl.glPushMatrix();

 		if(tire_comp[i]==0){
			wheelPosition.set(r.rigidBodyPoints[i].cpy().add(inverseUpVector.cpy().mul(2-tire_comp[i])));
			wheelPosition.mul(vehicleState.localToWorldMatrix);}
 		else
 			wheelPosition.set(	raycastWheels[i].intersectionPoint);
			gl.glTranslatef(wheelPosition.x, wheelPosition.y, wheelPosition.z);
			gl.glMultMatrixf(vehicleState.orientationMatrix.cpy().val, 0);
			gl.glRotatef(wheelRotationAngle, raycastWheels[i].sideVec.x, raycastWheels[i].sideVec.y, raycastWheels[i].sideVec.z);
			if (i < 2) {
				gl.glRotatef(r.steerAngle * MathUtils.radiansToDegrees, raycastWheels[i].upVec.x, raycastWheels[i].upVec.y, raycastWheels[i].upVec.z);

			}

			wheelMesh.render(GL10.GL_TRIANGLES);
			gl.glPopMatrix();
		}
	}

	Vector3 vehiclePosition = new Vector3();

	private void renderCar(GL10 gl, RigidBodyState vehicleState, float carAngle) {
		gl.glPushMatrix();
		carTexture.bind();

		// Vector3 vehiclePosition = new
		// Vector3().set(vehicleState.position).add(inverseUpVector.cpy().mul(-2));
		gl.glTranslatef(vehicleState.position.x, vehicleState.position.y, vehicleState.position.z);

		gl.glMultMatrixf(vehicleState.orientationMatrix.cpy().val, 0);
		carMesh.render(GL10.GL_TRIANGLES);
		gl.glPopMatrix();
	}

	private void renderScene(GL10 gl, Mesh terrain) {
		sandboxTexture.bind();
		gl.glPushMatrix();
		terrain.render(GL10.GL_TRIANGLES);
		gl.glPopMatrix();
	}

	private void setProjectionAndCamera(GL10 gl, Vector3 position) {

		// camera.position.add(camera3D.upVec.tmp().nor().mul(2));

		camera.position.set(position.x, position.y, position.z);
		camera.position.add(0, 10, 30);
		camera.lookAt(position.x, position.y, position.z);
		camera.update();
		camera.apply(gl);
		// camera.direction.set(0, 0, 0).sub(camera.position).nor();
	}

	float[] direction = { 1, 0.5f, 0, 0 };

	private void setLighting(GL10 gl, boolean enabled) {
		if (enabled) {
			gl.glEnable(GL10.GL_LIGHTING);
			gl.glEnable(GL10.GL_LIGHT0);
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, direction, 0);
			gl.glEnable(GL10.GL_COLOR_MATERIAL);
		} else {
			gl.glDisable(GL10.GL_LIGHTING);
			gl.glDisable(GL10.GL_LIGHT0);
			gl.glDisable(GL10.GL_COLOR_MATERIAL);
		}
	}

	public void dispose() {
		constraintSphereMesh.dispose();
		carTexture.dispose();
		planeMesh.dispose();
		sandboxTexture.dispose();
		decalBatch.dispose();
		starfield.dispose();
		streetMesh.dispose();
	}

}
